package org.biubiu0002.baiblog.core.service.impl;

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.TypeReference;
import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import okhttp3.Response;
import org.apache.commons.lang3.StringUtils;
import org.biubiu0002.baiblog.common.constants.OAuthConstants;
import org.biubiu0002.baiblog.common.constants.UserConstants;
import org.biubiu0002.baiblog.common.exception.BaiBlogException;
import org.biubiu0002.baiblog.common.exception.BizCodeEnumException;
import org.biubiu0002.baiblog.core.dao.ThirdAccountDao;
import org.biubiu0002.baiblog.core.dto.gitee.Code2TokenDto;
import org.biubiu0002.baiblog.core.dto.gitee.GiteeUserDto;
import org.biubiu0002.baiblog.core.model.entity.SysUserEntity;
import org.biubiu0002.baiblog.core.model.entity.ThirdAccountEntity;
import org.biubiu0002.baiblog.core.model.vo.req.RegisterReqVo;
import org.biubiu0002.baiblog.core.model.vo.res.LoginResVo;
import org.biubiu0002.baiblog.core.model.vo.res.RegisterResVo;
import org.biubiu0002.baiblog.core.service.AuthService;
import org.biubiu0002.baiblog.core.service.OAuthService;
import org.biubiu0002.baiblog.core.service.SysUserService;
import org.biubiu0002.baiblog.core.utils.HttpUtils;
import org.biubiu0002.baiblog.core.utils.IdGenerator;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import javax.annotation.Resource;
import java.io.IOException;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author: biubiu0002
 * @Date: 2022/11/2 12:43
 **/
@Service
@Slf4j
@Data
public class OAuthServiceImpl implements OAuthService {

    @Value("${oauth.gitee.client-id}")
    private String giteeClientId;

    @Value("${oauth.gitee.redirect-uri}")
    private String giteeRedirectUri;

    @Value("${oauth.gitee.client-secret}")
    private String giteeClientSecret;

    @Resource
    private AuthService authService;

    @Resource
    private SysUserService sysUserService;
    @Autowired
    private ThirdAccountDao thirdAccountDao;


    @Override
    @Transactional(rollbackFor = Exception.class)
    public LoginResVo giteeLogin(String code) {
        GiteeUserDto giteeUser ;
        try {
            giteeUser = this.getGiteeUser(code);
        } catch (Exception e) {

            throw new BaiBlogException("获取gitee用户失败", e);

        }
        //检查用户在不在系统中
        Integer giteeCode = OAuthConstants.TypeEnum.GITEE.getCode();
        Integer giteeUserId = giteeUser.getId();
        String giteeUserName = giteeUser.getName();
        ThirdAccountEntity thirdAccountEntity = thirdAccountDao.selectByTypeAndThirdId(giteeCode, Long.valueOf(giteeUserId));
        String uid;
        if (thirdAccountEntity == null) {
            //不在系统 注册账号
            RegisterReqVo registerReqVo = new RegisterReqVo();
            //生成用户名 考虑用户名冲突情况
            String generateUsername = this.generateUsername(giteeUserName);
            registerReqVo.setUsername(generateUsername);
            registerReqVo.setPassword(this.generatePassword(OAuthConstants.TypeEnum.GITEE));
            registerReqVo.setNickname(giteeUserName);
            registerReqVo.setAvatar(giteeUser.getAvatar_url());
            registerReqVo.setEmail(giteeUser.getEmail());
            registerReqVo.setUserType(UserConstants.UserTypeEnum.USER);
            RegisterResVo registerResVo = authService.register(registerReqVo);
            String username = registerResVo.getUsername();
            if (!username.equals(generateUsername)) {
                throw new BaiBlogException(BizCodeEnumException.OAUTH_GITEE_USERNAME_COLLISION_ERROR);
            }
            uid = registerResVo.getUserUid();
            thirdAccountEntity = new ThirdAccountEntity();
            thirdAccountEntity.setId(IdGenerator.generateId());
            thirdAccountEntity.setUserId(uid);
            thirdAccountEntity.setType(giteeCode);
            thirdAccountEntity.setThirdId(Long.valueOf(giteeUserId));
            thirdAccountEntity.setVerify(true);
            thirdAccountDao.bindUser(thirdAccountEntity);
        } else {
            //在系统 放行登录
            uid = thirdAccountEntity.getUserId();
        }
        //登录逻辑
        return authService.doLogin(uid);
    }


    /**
     * 生成三方账号的系统用户名 考虑用户名冲突的情况
     *
     * @param name 三方账号用户名
     * @return 用户名
     */
    private String generateUsername(String name) {
        SysUserEntity userEntity = authService.getByUsername(name);
        if (userEntity == null) {
            return name;
        } else {
            List<SysUserEntity> userEntityList = authService.getByUsernameLike(name);
            if (CollectionUtils.isEmpty(userEntityList)) {
                return name + "-2";
            }
            Set<String> usernameSet = userEntityList.stream().map(SysUserEntity::getUsername).collect(Collectors.toSet());
            StringBuilder sb = new StringBuilder(name);
            sb.append("-");
            for (int i = 3; i < Integer.MAX_VALUE; i++) {
                sb.append(i);
                if (!usernameSet.contains(sb.toString())) {
                    return sb.toString();
                }
                sb.delete(name.length(), sb.length());
            }
            return name;
        }
    }

    /**
     * 生成三方账号的系统密码 考虑用户名冲突的情况
     *
     * @param typeEnum      三方账号平台
     * @return 密码
     */
    private String generatePassword(OAuthConstants.TypeEnum typeEnum) {
        return typeEnum.name() + "@" +  IdGenerator.generateUid().substring(0,5);
    }

    private GiteeUserDto getGiteeUser(String code) throws IOException {
        String code2TokenRequestUrl = OAuthConstants.Gitee.getCode2TokenRequestUrl(code, giteeClientId, giteeRedirectUri, giteeClientSecret);
        Response code2TokenResponse = HttpUtils.post(code2TokenRequestUrl);
        if (code2TokenResponse.code() != 200) {
            throw new BaiBlogException(BizCodeEnumException.OAUTH_GITEE_CODE_ERROR);
        }

        assert code2TokenResponse.body() != null;
        String code2TokenJson = code2TokenResponse.body().string();
        Code2TokenDto code2TokenDto = JSON.parseObject(code2TokenJson, new TypeReference<Code2TokenDto>() {
        });
        String accessToken = code2TokenDto.getAccess_token();
        String refreshToken = code2TokenDto.getRefresh_token();
        if (StringUtils.isBlank(accessToken) || StringUtils.isBlank(refreshToken)) {
            throw new BaiBlogException(BizCodeEnumException.OAUTH_GITEE_USER_ERROR);
        }
        String userInfoJson;
        GiteeUserDto giteeUserDto;
        String userInfoRequestUrl = OAuthConstants.Gitee.getUserInfoRequestUrl(accessToken);
        try {
            Response userInfoResponse = HttpUtils.get(userInfoRequestUrl);
            if (userInfoResponse.code() != 200) {
                throw new BaiBlogException(BizCodeEnumException.OAUTH_GITEE_USER_ERROR);
            }
            userInfoJson = userInfoResponse.body().string();
            giteeUserDto = JSON.parseObject(userInfoJson, new TypeReference<GiteeUserDto>() {
            });
        } catch (Exception e) {
            //accessToken失效 刷新token重新获取accessToken 再获取用户信息
            log.info("accessToken失效");
            String refreshTokenRequestUrl = OAuthConstants.Gitee.getRefreshTokenRequestUrl(refreshToken);
            Response refreshTokenResponse = HttpUtils.post(refreshTokenRequestUrl);
            if (!refreshTokenResponse.isSuccessful()) {
                throw new BaiBlogException(BizCodeEnumException.OAUTH_GITEE_REFRESH_TOKEN_ERROR);
            }
            assert refreshTokenResponse.body() != null;
            String refreshTokenJson = refreshTokenResponse.body().string();
            // 从这个json中提取新的accessToken
            code2TokenDto = JSON.parseObject(refreshTokenJson, new TypeReference<Code2TokenDto>() {
            });
            accessToken = code2TokenDto.getAccess_token();
            userInfoRequestUrl = OAuthConstants.Gitee.getUserInfoRequestUrl(accessToken);
            Response userInfoResponse = HttpUtils.get(userInfoRequestUrl);
            if (userInfoResponse.code() != 200) {
                throw new BaiBlogException(BizCodeEnumException.OAUTH_GITEE_USER_ERROR);
            }
            assert userInfoResponse.body() != null;
            userInfoJson = userInfoResponse.body().string();
            giteeUserDto = JSON.parseObject(userInfoJson, new TypeReference<GiteeUserDto>() {
            });
        }
        return giteeUserDto;
    }


}
